﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;

namespace Brjnk.GpxExplorer.Workspace.BitmapExport
{
    public class LabelsGroup : INotifyPropertyChanged
    {
        private List<LabelBase> labels = new List<LabelBase>();

        public LabelsGroup()
        {
        }

        public void AddLabel(LabelBase label)
        {
            labels.Add(label);
        }

        public void RemoveLabel(LabelBase label)
        {
            labels.Remove(label);
        }

        public void Move(LabelBase label, Vector v)
        {
            switch (Mode)
            {
                case LabelsGroupMode.Disabled:
                    break;

                case LabelsGroupMode.Enabled:
                    RefreshEnabledMode(label, v);
                    break;

                case LabelsGroupMode.Vertical:
                    RefreshVerticalMode(label);
                    break;

                case LabelsGroupMode.Horizontal:
                    RefreshHorizontalMode(label);
                    break;

                default:
                    break;
            }
        }

        public void Refresh()
        {
            if (labels.Count == 0) return;
            var label = labels.First();
            switch (Mode)
            {
                case LabelsGroupMode.Disabled:
                    break;

                case LabelsGroupMode.Enabled:
                    break;

                case LabelsGroupMode.Vertical:
                    RefreshVerticalMode(label);
                    break;

                case LabelsGroupMode.Horizontal:
                    RefreshHorizontalMode(label);
                    break;

                default:
                    break;
            }
        }

        private void RefreshHorizontalMode(LabelBase label)
        {
            int index = labels.IndexOf(label);
            if (index < 0) throw new InvalidOperationException("Unknown error");
            RefreshHorizontalOrVerticalMode(index,
                (previous, current) =>
                    new Point(previous.TargetPoint.X - previous.SizeLeft - current.SizeRight, previous.TargetPoint.Y),
                (previous, current) =>
                    new Point(previous.TargetPoint.X + previous.SizeRight + current.SizeLeft, previous.TargetPoint.Y)
                );
        }

        private void RefreshVerticalMode(LabelBase label)
        {
            int index = labels.IndexOf(label);
            if (index < 0) throw new InvalidOperationException("Unknown error");

            RefreshHorizontalOrVerticalMode(index,
     (previous, current) =>
        new Point(previous.TargetPoint.X, previous.TargetPoint.Y - previous.SizeTop - current.SizeBottom),
    (previous, current) =>
        new Point(previous.TargetPoint.X, previous.TargetPoint.Y + previous.SizeBottom + current.SizeTop)
    );

            foreach (var l in labels)
                System.Diagnostics.Debug.WriteLine(l.TargetPoint.X);
        }

        private delegate Point TargetPointProvider(LabelBase previous, LabelBase current);

        private void RefreshHorizontalOrVerticalMode(int index, TargetPointProvider targetPointProviderNeg, TargetPointProvider targetPointProviderPos)
        {
            var previous = labels[index];
            for (int i = index - 1; i >= 0; i--)
            {
                var current = labels[i];
                current.TargetPoint = targetPointProviderNeg(previous, current);
                previous = current;
            }

            previous = labels[index];

            for (int i = index + 1; i < labels.Count; i++)
            {
                var current = labels[i];
                current.TargetPoint = targetPointProviderPos(previous, current);
                previous = current;
            }
        }

        private void RefreshEnabledMode(LabelBase label, Vector v)
        {
            foreach (var l in labels)
            {
                if (!object.ReferenceEquals(l, label))
                    l.TargetPoint += v;
            }
        }

        private void ChangeMode()
        {
            if (Mode == LabelsGroupMode.Horizontal)
                InitializeHorizontal();
            else if (Mode == LabelsGroupMode.Vertical)
                InitializeVertical();
        }

        private void InitializeHorizontal()
        {
            if (labels.Count == 0) return;
            labels.Sort((l1, l2) => (int)(l1.TargetPoint.X - l2.TargetPoint.X));
            RefreshHorizontalMode(labels[0]);
        }

        private void InitializeVertical()
        {
            if (labels.Count == 0) return;
            labels.Sort((l1, l2) => (int)(l1.TargetPoint.Y - l2.TargetPoint.Y));
            RefreshVerticalMode(labels[0]);
        }

        private LabelsGroupMode mode;

        public LabelsGroupMode Mode
        {
            get { return mode; }
            set { mode = value; ChangeMode(); OnPropertyChanged("Mode"); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public enum LabelsGroupMode
    {
        Disabled,
        Enabled,
        Vertical,
        Horizontal
    }
}